/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.logisim.circuit;

import java.awt.Color;
import java.awt.Graphics;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.UItools.GraphicsUtil;

/**
 * Maintains a collection of <code>CircuitState</code> and <code>Location</code> objects and
 * includes a draw method for these objects.
 */
class PropagationPoints {
	
	/**
	 * Contains information about a <code>CircuitState</code> and a <code>Location</code>.
	 */
    private static class Entry {
        private CircuitState state;
        private Location loc;
       
        /**
         * Initialize the <code>CircuitState</code> and the <code>Location</code>.
         * @param state <code>CircuitState</code>
         * @param loc <code>Location</code>
         */
        private Entry(CircuitState state, Location loc) {
            this.state = state;
            this.loc = loc;
        }
        
        /**
         * Returns true if the parameter is an <code>Entry</code> and the <code>CircuitState</code>
         * and <code>Location</code> are the same.
         * @param other <code>Object</code> to compare if it is an <code>Entry</code>
         * @return <code>boolean</code>
         */
        public boolean equals(Object other) {
            if(!(other instanceof Entry)) return false;
            Entry o = (Entry) other;
            return state.equals(o.state) && loc.equals(o.loc);
        }
        
        /**
         * Returns a hash code for the <code>Entry</code> based off of the <code>CircuitState</code>
         * and <code>Location</code>.
         * @return <code>int</code> hash code
         */
        public int hashCode() {
            return state.hashCode() * 31 + loc.hashCode();
        }
    }
    
    private HashSet data;
    
    /**
     * Initialize by creating a new <code>HashSet</code> for <code>Entry</code> objects.
     */
    PropagationPoints() {
        this.data = new HashSet();
    }
    
    /**
     * Adds a <code>CircuitState</code> and <code>Location</code> to the <code>PropagationPoints</code>.
     * @param state <code>CircuitState</code>
     * @param loc <code>Location</code>
     */
    void add(CircuitState state, Location loc) {
        data.add(new Entry(state, loc));
    }
    
    /**
     * Clears all <code>Entry<code> objects. 
     * i.e. all <code>CircuitState</code> and <code>Location</code> objects.
     */
    void clear() {
        data.clear();
    }
    
    /**
     * Indicates if there are any <code>Entry</code> objects in this class.
     * @return <code>boolean</code> true if there are no <code>Entry</code> objects
     */
    boolean isEmpty() {
        return data.isEmpty();
    }
    
    /**
     * Draw all components contained within this class and all components contained within the 
     * <code>ComponentDrawContext</code>.
     * @param context <code>ComponentDrawContext</code> containing <code>CircuitStates</code> 
     */
    void draw(ComponentDrawContext context) {
        if(data.isEmpty()) return;
        
        CircuitState state = context.getCircuitState();
        HashMap stateMap = new HashMap();
        for(Iterator it = state.getSubstates().iterator(); it.hasNext(); ) {
            CircuitState s = (CircuitState) it.next();
            addSubstates(stateMap, s, s);
        }
        
        Graphics g = context.getGraphics();
        GraphicsUtil.switchToWidth(g, 2);
        for(Iterator it = data.iterator(); it.hasNext(); ) {
            Entry e = (Entry) it.next();
            if(e.state == state) {
                Location p = e.loc;
                g.drawOval(p.getX() - 4, p.getY() - 4, 8, 8);
            } else if(stateMap.containsKey(e.state)) {
                CircuitState substate = (CircuitState) stateMap.get(e.state);
                Subcircuit subcirc = substate.getSubcircuit();
                Bounds b = subcirc.getBounds();
                g.drawRect(b.getX(), b.getY(), b.getWidth(), b.getHeight());
            }
        }
        GraphicsUtil.switchToWidth(g, 1);
    }
    
    /**
     * Adds all <code>CircuitState</code> objects and all embedded <code>CircuitState</code> objects to the 
     * <code>HashMap</code>.
     * @param map <code>HashMap</code> where <code>CircuitState</code> objects are added
     * @param source <code>CircuitState</code> key for the <code>HashMap</code>
     * @param value <code>CircuitState</code> value for the <code>HashMap</code>
     */
    private void addSubstates(HashMap map, CircuitState source, CircuitState value) {
        map.put(source, value);
        for(Iterator it = source.getSubstates().iterator(); it.hasNext(); ) {
            CircuitState s = (CircuitState) it.next();
            addSubstates(map, s, value);
        }
    }
}
